package com.pig4cloud.pigx.contract.api.v1.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.pig4cloud.pigx.common.core.util.R;
import com.pig4cloud.pigx.common.customize.api.enums.AbnormalEnum;
import com.pig4cloud.pigx.common.customize.api.util.OpenApiUtil;
import com.pig4cloud.pigx.common.customize.contants.MongoTableNameConstants;
import com.pig4cloud.pigx.contract.api.v1.service.SymbolServiceV1;
import com.pig4cloud.pigx.contract.api.v1.utils.OpenApiContractUtilsV1;
import com.pig4cloud.pigx.contract.entity.ContractSymbol;
import com.pig4cloud.pigx.contract.enums.ContractOrderEnum;
import com.pig4cloud.pigx.contract.mapper.ContractSymbolMapper;
import com.pig4cloud.pigx.contract.mongo.entity.ContractSymbolMongo;
import com.pig4cloud.pigx.contract.open.api.contants.OpenApiRedisConstantsV1;
import com.pig4cloud.pigx.contract.open.api.vo.OpenApiSymbol24hTickerVOV1;
import com.pig4cloud.pigx.contract.open.api.vo.OpenApiSymbolDetailsVOV1;
import com.pig4cloud.pigx.contract.open.api.vo.OpenApiSymbolHandlingRateVOV1;
import com.pig4cloud.pigx.contract.open.api.vo.OpenApiSymbolHierarchyVOV1;
import lombok.AllArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Comparator;
import java.util.List;
import java.util.NavigableMap;
import java.util.TreeMap;

/**
 */
@Service
@AllArgsConstructor
public class SymbolServiceImplV1 extends ServiceImpl<ContractSymbolMapper, ContractSymbol> implements SymbolServiceV1 {

	private final MongoTemplate mongo;

	private final OpenApiContractUtilsV1 utils;

	private final RedisTemplate redis;

	@Override
	public R<OpenApiSymbolDetailsVOV1> getSymbol(String symbolName) {
		// 获取币对信息
		ContractSymbolMongo symbol = utils.getSymbolMongo(symbolName);
		// 类型转换
		OpenApiSymbolDetailsVOV1 dto = new OpenApiSymbolDetailsVOV1();
		try {
			BeanUtils.copyProperties(symbol, dto);
		} catch (Exception e) {
			// 类型转换异常 服务器错误
			throw OpenApiUtil.getError(AbnormalEnum.MSG.DISCONNECTED);
		}
		// 查询币对层级
		try {
			Query query = new Query();
			query.addCriteria(Criteria.where("symbolName").is(symbolName));
			query.with(Sort.by(Sort.Order.asc("hierarchy")));
			List<OpenApiSymbolHierarchyVOV1> symbolHierarchy = mongo.find(
					query,
					OpenApiSymbolHierarchyVOV1.class,
					MongoTableNameConstants.CONTRACT_SYMBOL_HIERARCHY
			);
			dto.setSymbolHierarchy(symbolHierarchy);
			return R.ok(dto);
		} catch (Exception e) {
			return R.ok(dto);
		}
	}

	@Override
	public R<BigDecimal> getSymbolLastPrice(String symbolName) {
		return R.ok(utils.getSymbolPrice(symbolName, OpenApiRedisConstantsV1.LAST_PRICE));
	}

	@Override
	public R<BigDecimal> getSymbolIndexPrice(String symbolName) {
		return R.ok(utils.getSymbolPrice(symbolName, OpenApiRedisConstantsV1.INDEX_PRICE));
	}

	@Override
	public R<BigDecimal> getSymbolMarkPrice(String symbolName) {
		return R.ok(utils.getSymbolPrice(symbolName, OpenApiRedisConstantsV1.MARK_PRICE));
	}

	@Override
	public R<OpenApiSymbol24hTickerVOV1> getSymbol24hTicker(String symbolName) {
		ContractSymbolMongo symbol = utils.getSymbolMongo(symbolName);
		try {
			// 获取币对信息
			OpenApiSymbol24hTickerVOV1 vo = new OpenApiSymbol24hTickerVOV1();
			BeanUtils.copyProperties(symbol, vo);
			return R.ok(vo);
		} catch (Exception e) {
			throw OpenApiUtil.getError(AbnormalEnum.MSG.DISCONNECTED);
		}
	}

	@Override
	public R<OpenApiSymbolHandlingRateVOV1> getSymbolHandlingRate(String symbolName) {
		// 获取币对信息
		ContractSymbolMongo symbol = utils.getSymbolMongo(symbolName);
		try {
			// 获取币对信息
			OpenApiSymbolHandlingRateVOV1 vo = new OpenApiSymbolHandlingRateVOV1();
			BeanUtils.copyProperties(symbol, vo);
			vo.setSymbolName(symbolName);
			return R.ok(vo);
		} catch (Exception e) {
			throw OpenApiUtil.getError(AbnormalEnum.MSG.DISCONNECTED);
		}
	}


	/**
	 * 获取币对深度图
	 *
	 * @param symbolName 币对名称
	 * @param level      币对级别
	 * @return r
	 */
	@Override
	public R<JSONObject> getSymbolDepth(String symbolName, String level) {
		if (level == null || "".equals(level)) {
			throw OpenApiUtil.getError(AbnormalEnum.MSG.MANDATORY_PARAM_EMPTY_OR_MALFORMED, "'level'");
		}
		List<String> list = this.getLevelFromRedis(symbolName).getData();
		if (!list.contains(level)) {
			throw OpenApiUtil.getError(AbnormalEnum.MSG.UNKNOWN_ORDER_COMPOSITION, "'level is wrongful'");
		}
		try {
			TreeMap<BigDecimal, List<BigDecimal>> moreTreeMap = getDepthFromRedis(symbolName, ContractOrderEnum.Side.MORE.getValue(), level);
			TreeMap<BigDecimal, List<BigDecimal>> emptyTreeMap = getDepthFromRedis(symbolName, ContractOrderEnum.Side.EMPTY.getValue(), level);

			NavigableMap<BigDecimal, List<BigDecimal>> moreMap = moreTreeMap.descendingMap();
			NavigableMap<BigDecimal, List<BigDecimal>> emptyMap = emptyTreeMap.descendingMap();

			JSONObject depthJsonObject = new JSONObject();
			JSONArray jsonArrayMore = new JSONArray();
			final BigDecimal t = moreMap.get(moreMap.lastKey()).get(1);
			moreMap.forEach((k, v) -> {
				JSONArray arr = new JSONArray();
				arr.add(k);
				arr.add(v.get(0));
				arr.add(v.get(1));
				arr.add(v.get(1).divide(t, 4, RoundingMode.DOWN));
				jsonArrayMore.add(arr);
			});
			depthJsonObject.put(ContractOrderEnum.Side.MORE.getValue(), jsonArrayMore);

			JSONArray jsonArrayEmpty = new JSONArray();
			final BigDecimal tm = emptyMap.get(emptyMap.firstKey()).get(1);
			emptyMap.forEach((k, v) -> {
				JSONArray arr = new JSONArray();
				arr.add(k);
				arr.add(v.get(0));
				arr.add(v.get(1));
				arr.add(v.get(1).divide(tm, 4, RoundingMode.DOWN));
				jsonArrayEmpty.add(arr);
			});

			depthJsonObject.put(ContractOrderEnum.Side.EMPTY.getValue(), jsonArrayEmpty);

			return R.ok(depthJsonObject);
		} catch (Exception e) {
			throw OpenApiUtil.getError(AbnormalEnum.MSG.DISCONNECTED);
		}
	}

	private TreeMap<BigDecimal, List<BigDecimal>> getDepthFromRedis(String symbol, String side, String level) {
		TreeMap<BigDecimal, List<BigDecimal>> depTreeMap = new TreeMap<BigDecimal, List<BigDecimal>>();
		Object o = redis.opsForValue().get("depth:" + symbol + ":" + side + ":" + level);
		if (o != null) {
			JSONObject jsonObject = JSONObject.parseObject(o.toString());
			Gson gson = new Gson();
			depTreeMap = gson.fromJson(jsonObject.toJSONString(), new TypeToken<TreeMap<BigDecimal, List<BigDecimal>>>() {
			}.getType());
		}
		return depTreeMap;
	}

	/**
	 * 获取币对的深度级别
	 *
	 * @param symbolName 币对名称
	 * @return r
	 */
	@Override
	public R<List<String>> getLevelFromRedis(String symbolName) {
		utils.getSymbolMongo(symbolName);
		try {
			String key = "depth:" + symbolName + ":levellist";
			List<String> list = redis.opsForList().range(key, 0, -1);
			assert list != null;
			list.sort(Comparator.naturalOrder());
			return R.ok(list);
		} catch (Exception e) {
			throw OpenApiUtil.getError(AbnormalEnum.MSG.DISCONNECTED);
		}
	}


}
